//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
//==
//==
//==
//==================================================================

#include "stdafx.h"
#include <math.h>
#include <string.h>
#include "lightmap.h"

//#include "rgbe.h"

//#define DEBUG_CROSS

//===============================================================================
// harmonics coefficient indices
enum {
	K00 =0,
	K11 ,
	K10 ,
	K1_1,
	K21 ,
	K2_1,
	K2_2,
	K20 ,
	K22
};

//===============================================================================
// render coefficients
#define C1	0.429043
#define C2	0.511664
#define C3	0.743125
#define C4	0.886227
#define C5	0.247708

//==================================================================
//#define ANG_MAP_DIM			24

//==================================================================
LightMap::LightMap() :
	_shcube( FACE_SIZE )
{
	for (int i=0; i < 6; ++i)
		_color_maps[i].Setup( ImageBase::BF_R32FG32FB32F, FACE_SIZE, FACE_SIZE, NULL, 0 );
}

//===============================================================================
static void build_rgb_matrices( Matrix44 *matsp, float L[3][9] )
{
	for (int i=0; i < 3; ++i)
	{
		Matrix44	&m = matsp[i];

		float *Lp = L[i];

		m._m[0][0] = C1 * Lp[K22];	
		m._m[0][1] = C1 * Lp[K2_2];
		m._m[0][2] = C1 * Lp[K21];
		m._m[0][3] = C2 * Lp[K11];

		m._m[1][0] = C1 * Lp[K2_2];
		m._m[1][1] =-C1 * Lp[K22];
		m._m[1][2] = C1 * Lp[K2_1];
		m._m[1][3] = C2 * Lp[K1_1];

		m._m[2][0] = C1 * Lp[K21];
		m._m[2][1] = C1 * Lp[K2_1];
		m._m[2][2] = C3 * Lp[K20];
		m._m[2][3] = C2 * Lp[K10];

		m._m[3][0] = C2 * Lp[K11];
		m._m[3][1] = C2 * Lp[K1_1];
		m._m[3][2] = C2 * Lp[K10];
		m._m[3][3] = C4 * Lp[K00] - C5 * Lp[K20];
	}
}

//==================================================================
static int draw_rect_float( ImageBase &img, Vector3 col, int x, int y, int w, int h )
{
	if ( x < 0 ){	w += x; x = 0; }
	if ( y < 0 ){	h += y; y = 0; }

	if ( w <= 0 )	return 0;
	if ( h <= 0 )	return 0;

	int	img_w = img.GetWidth();
	int	img_h = img.GetHeight();

	if ( x >= img_w )	return 0;
	if ( y >= img_h )	return 0;

	if ( x+w > img_w )	w = img_w - x;
	if ( y+h > img_h )	h = img_h - y;


	float *p1 = (float *)(img.GetData() + img.GetImagePitch() * y);
	for (int yc=h; yc > 0; --yc, p1 = (float *)((u_char *)p1 + img.GetImagePitch()))
	{
		Vector3 *p2 = (Vector3 *)(p1 + x * 3);
		for (int xc=w; xc > 0; --xc)
		{
			*p2++ += col;
		}
	}

	return 1;
}

//==================================================================
static void clear_bmp( ImageBase &img )
{
	memset( img.GetData(), 0, img.GetImagePitch() * img.GetHeight() );
}

//==================================================================
void LightMap::ResetLights()
{
	for (int i=0; i < 6; ++i)
		clear_bmp( _color_maps[ i ] );
}

//==================================================================
void LightMap::drawPixel( const Vector3 &col, int face, int x, int y )
{
	draw_rect_float( _color_maps[ face ], col, x, y, 1, 1 );
}

//==================================================================
void LightMap::DrawLight( const Vector3 &col, const Vector4 &dir )
{
/*
			TOP		(2,1,0)
LEFT(0,0,1)	FRONT	(5,1,1)	RIGHT(1,2,1)
			BOTTOM	(3,1,2)
			BACK	(4,1,3)
*/
//	static int			u=5, v=5;
//	static int			face=5;

	int			u, v;
	int			face;

	face = SHCube::FireSpecularRay_s( Vector3( dir._x, dir._y, dir._z ), &u, &v );

	u = u * (_color_maps[ face ].GetWidth()-1) / 32768;
	v = v * (_color_maps[ face ].GetHeight()-1) / 32768;

	drawPixel( col, face, u, v );
}

//==================================================================
void LightMap::DrawLight( const Vector3 &col, float the, float phi )
{
	Vector4	dir(	sinf( the ) * cosf( phi ),
					sinf( the ) * sinf( phi ),
					cosf( the ),
					0 );

	DrawLight( col, dir );
}

//==================================================================
void LightMap::Update( const Matrix44 *space_transform_mp )
{
	ImageBase	*faces_listp[6];
	for (int i=0; i < 6; ++i)
		faces_listp[i] = &_color_maps[i];

	float		sh_coeffs[3][9];
	_shcube.Integrate( sh_coeffs, faces_listp );

	build_rgb_matrices( _shlight_mats, sh_coeffs );

	if ( space_transform_mp )
	{
		const Matrix44	&A_B_m = *space_transform_mp;
		Matrix44		B_A_m = A_B_m.Transpose33();

		for (int i=0; i < 3; ++i)
			_shlight_mats[i] = A_B_m * _shlight_mats[i] * B_A_m;
	}
}
